// 这个示例程序展示如何将一个类型嵌入另一个类型，以及
// 内部类型和外部类型之间的关系
package main

import (
	"fmt"
)

// user 在程序里定义一个用户类型
type user struct {
	name  string
	email string
}

// notify 实现了一个可以通过user 类型值的指针
// 调用的方法
func (u *user) notify() {
	fmt.Printf("Sending user email to %s<%s>\n",
		u.name,
		u.email)
}

// admin 代表一个拥有权限的管理员用户
type admin struct {
	user  // 嵌入类型
	level string
}

/**
Go语言允许用户扩展或者修改已有类型的行为。这个功能对代码复用很重要，在修改已有类型以符合新类型的时候也很重要。这个功能是通过嵌入
类型（type embedding）完成的。嵌入类型是将已有的类型直接声明在新的结构类型里。被嵌入的类型被称为新的外部类型的内部类型。

通过嵌入类型，与内部类型相关的标识符会提升到外部类型上。这些被提升的标识符就像直接声明在外部类型里的标识符一样，也是外部类型的一
部分。这样外部类型就组合了内部类型包含的所有属性，并且可以添加新的字段和方法。外部类型也可以通过声明与内部类型标识符同名的标识符
来覆盖内部标识符的字段或者方法。这就是扩展或者修改已有类型的方法。
*/
func main() {
	// 创建一个admin 用户
	ad := admin{
		user: user{
			name:  "john smith",
			email: "john@yahoo.com",
		},
		level: "super",
	}

	// 我们可以直接访问内部类型的方法
	ad.user.notify()

	// 内部类型的方法也被提升到外部类型
	ad.notify()
}

/**
在上面代码中，我们的程序演示了如何嵌入一个类型，并访问嵌入类型的标识符。我们从第 10 行和第 24 行中的两个结构类型的声明开始。在第 10 行，
我们声明了一个名为 user 的结构类型。在第 24 行，我们声明了另一个名为 admin 的结构类型。在声明 admin 类型的第 25 行，我们将 user 类型嵌
入 admin 类型里。

要嵌入一个类型，只需要声明这个类型的名字就可以了。在第 26 行，我们声明了一个名为 level 的字段。注意声明字段和嵌入类型在语法上的不同。

一旦我们将 user 类型嵌入 admin，我们就可以说 user 是外部类型 admin 的内部类型。有了内部类型和外部类型这两个概念，就能更容易地理解这两种
类型之间的关系。

代码的 17~21 行，展示了使用 user 类型的指针接收者声明名为 notify 的方法。这个方法只是显示一行友好的信息，表示将邮件发给了特定的用户以及邮
件地址。

现在，让我们来看一下 main 函数，在 main 函数中展示了嵌入类型背后的机制。在第 32 行，创建了一个 admin 类型的值。内部类型的初始化是用结构字
面量完成的。通过内部类型的名字可以访问内部类型。

对外部类型来说，内部类型总是存在的。这就意味着，虽然没有指定内部类型对应的字段名，还是可以使用内部类型的类型名，来访问到内部类型的值。

在代码的第 41 行，可以看到对 notify 方法的调用。这个调用是通过直接访问内部类型 user 来完成的。这展示了内部类型是如何存在于外部类型内，并且
总是可访问的。不过，借助内部类型提升，notify 方法也可以直接通过 ad 变量来访问。

代码中的第 44 行中展示了直接通过外部类型的变量来调用 notify 方法。由于内部类型的标识符提升到了外部类型，我们可以直接通过外部类型的值来访问内
部类型的标识符。让我们修改一下这个例子，加入一个接口，代码如下所示。
*/
